<?php
// +----------------------------------------------------------------------
// | webhooks
// +----------------------------------------------------------------------
// | Author: King east <1207877378@qq.com>
// +----------------------------------------------------------------------


namespace ke\route;


use FastRoute\DataGenerator\GroupCountBased;
use FastRoute\Dispatcher;
use FastRoute\RouteCollector;
use FastRoute\RouteParser\Std;

/**
 * Class RouteHandle
 * @package ke\route
 * @method RouteItem get($rule, $controller)
 * @method RouteItem post($rule, $controller)
 * @method RouteItem patch($rule, $controller)
 * @method RouteItem put($rule, $controller)
 * @method RouteItem delete($rule, $controller)
 * @method RouteItem head($rule, $controller)
 */
class RouteHandle
{
    private $route;

    static $METHODS = ['rule', 'get', 'post', 'patch', 'put', 'delete', 'head'];

    /**
     * @var RouteGroup $group
     */
    private $group;

    public function __construct()
    {
        $this->route = new RouteCollector(new Std(), new GroupCountBased());
    }

    public function __call($name, $arguments)
    {
        if (in_array($name, static::$METHODS)) {
            $handle = new RouteItem($arguments[1]);
            if ($this->group) $handle->setPrefix($this->group->getNamespace());

            $method = $name === 'rule' ? array_map('strtoupper', static::$METHODS) : strtoupper($name);

            $this->route->addRoute($method, ($this->group ? $this->group->getPrefix() : '') . $arguments[0], $handle);
            return $handle;
        }
    }

    /**
     * 路由分组
     * @param callable $callback
     */
    public function group(array $options, callable $callback)
    {
        $this->group = new RouteGroup($callback);
        foreach ($options as $option=>$v) {
            $n = 'set' . ucwords($option);
            $this->group->$n($v);
        }
        $this->group->call();
        $this->group = null;
    }

    /**
     * 启动路由调度
     */
    public function displatch()
    {
        $dispatcher = new Dispatcher\GroupCountBased($this->route->getData());
        $routeInfo = $dispatcher->dispatch($_SERVER['REQUEST_METHOD'], '/' . ($_GET['s'] ?? ''));
        switch ($routeInfo[0]) {
            case Dispatcher::NOT_FOUND:
                http_response_code(404);
                if (is_ajax()) {
                    throw new \Exception('404 Not Found');
                } else {
                    require ROOT_PATH . 'framework/tpl/404.php';
                    exit;
                }
                break;
            case Dispatcher::METHOD_NOT_ALLOWED:
                http_response_code(405);
                if (is_ajax()) {
                    throw new \Exception('405 Not Allowed');
                } else {
                    require ROOT_PATH . 'framework/tpl/404.php';
                    exit;
                }
                break;
            case Dispatcher::FOUND:

                // 开始解析控制器路径
                if (strpos($routeInfo[1]->_controller, '.') === false) {
                    $controller = $routeInfo[1]->_controller;
                    $action = 'index';
                } else {
                    list($controller, $action) = explode('.', $routeInfo[1]->_controller);
                }
                if (strpos($controller, '/') === false) {
                    $controller = ucwords($controller);
                } else {
                    $controller = preg_replace_callback('/\/(\w+)$/', function ($str) {
                        return '/' . ucwords($str[1]);
                    }, $controller);
                }
                $controller = str_replace('/', '\\', $controller);
                $vars = $routeInfo[2];

                $namespace = '\\app\\controller\\' . $controller . 'Controller';

                // 控制器实例
                $handle = new $namespace;
                $response = call_user_func([$handle, $action], $vars);

                // 适应输出格式
                if (is_array($response)) {
                    header('Content-Type: application/json;charset=utf-8');
                    echo json_encode($response);
                } else {
                    header('Content-Type: text/html;charset=utf-8');
                    echo $response;
                }
                break;
        }
    }
}